From c7826073decda498891c2f5f1e2a3561537e3394 Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Sat, 18 Jan 2020 14:46:24 -0700 Subject: [PATCH] eliminate cet usage in destinator, igo8, mmo. (#463) * eliminate cet usage in igo8 format. added test to verify written header. This test requires spaces to be embedded within option strings. testo had to be modififed to preserve these embedded spaces. * eliminate cet usage in destinator. * eliminate cet usage in mmo. This also fixes some apparent encoding/decoding bugs, although our understanding of this binary format is likely incomplete. * use smart pointer with encoders/decoders. * fix bad resolution of merge conflict. --- destinator.cc | 144 ++++++++++++++++++------------------ igo8.cc | 92 +++++++++-------------- lowranceusr.cc | 4 +- mmo.cc | 181 ++++++++++++++++++++++------------------------ testo | 4 +- testo.d/igo8.test | 4 + testo.d/mmo.test | 8 +- 7 files changed, 208 insertions(+), 229 deletions(-) diff --git a/destinator.cc b/destinator.cc index 4a31cdb05..34852c3c0 100644 --- a/destinator.cc +++ b/destinator.cc @@ -21,13 +21,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + +#include // for assert +#include // for fabs, lround +#include // for NULL, SEEK_CUR, snprintf +#include +#include // for strcmp, memmove, memset, strlen +#include // for gmtime + +#include // for QByteArray +#include // for QScopedPointer +#include // for QString +#include // for QTextCodec, QTextCodec::IgnoreHeader +#include // for QTextDecoder +#include // for QTextEncoder +#include // for QTime +#include // for QVector #include "defs.h" -#include "cet.h" -#include "cet_util.h" -#include "garmin_fs.h" -#include "strptime.h" -#include +#include "garmin_fs.h" // for garmin_fs_t, garmin_fs_flags_t, GMSD_GET, GMSD_SETSTRQ, garmin_fs_alloc, GMSD_FIND +#include "gbfile.h" // for gbfputdbl, gbfgetdbl, gbfputint32, gbfeof, gbfgetint32, gbfread, gbfrewind, gbfseek, gbfclose, gbfputflt, gbfgetc, gbfgetflt, gbfputc, gbfputcstr, gbfputint16, gbfwrite, gbfile, gbfopen_le +#include "src/core/datetime.h" // for DateTime +#include "strptime.h" // for strptime + #define MYNAME "destinator" #define DST_DYN_POI "Dynamic POI" @@ -39,6 +56,7 @@ QVector destinator_args = { static gbfile* fin, *fout; static gpsdata_type data_type; +static QTextCodec* utf16le_codec{nullptr}; /*******************************************************************************/ @@ -57,88 +75,66 @@ gmsd_init(Waypoint* wpt) } static QString -read_wcstr(const int discard) +read_wcstr() { - int16_t* buff = nullptr, c; - int size = 0, pos = 0; - - while (gbfread(&c, sizeof(c), 1, fin) && (c != 0)) { - if (size == 0) { - size = 16; - buff = (int16_t*) xmalloc(size * sizeof(*buff)); - } else if (pos == size) { - size += 16; - buff = (int16_t*) xrealloc(buff, size * sizeof(*buff)); - } - buff[pos] = c; - pos += 1; - } - - if (pos != 0) { - char* res; - if (discard) { - res = nullptr; + QScopedPointer decoder(utf16le_codec->makeDecoder(QTextCodec::IgnoreHeader)); + QString result; + bool done; + do { + QByteArray chunk = gbfreadbuf(2, fin); + assert(chunk.size() == 2); + if ((chunk.at(0) != 0) || (chunk.at(1) != 0)) { + result += decoder->toUnicode(chunk); + done = false; } else { - res = cet_str_uni_to_utf8(buff, pos); - res = lrtrim(res); - if (*res == '\0') { - xfree(res); - res = nullptr; - } + done = true; } - xfree(buff); - QString rv = QString::fromUtf8(res); - xfree(res); - return rv; - //return res; - } else { - return nullptr; - } + } while (!done); + return result.trimmed(); } static void write_wcstr(const QString& str) { - int len; - - short* unicode = cet_str_utf8_to_uni(CSTR(str), &len); - gbfwrite((void*)unicode, 2, len + 1, fout); - xfree(unicode); + /* use an encoder to avoid generating a BOM. */ + QScopedPointer encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader)); + QByteArray qba = encoder->fromUnicode(str).append(2, 0); + assert((qba.size() % 2) == 0); + gbfwrite(qba.constData(), 1, qba.size(), fout); } static int -read_until_wcstr(const char* str) +read_until_wcstr(const QString& str) { - int eos = 0, res = 0; + QScopedPointer encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader)); + QByteArray target = encoder->fromUnicode(str).append(2, 0); + assert((target.size() % 2) == 0); + + int eos = 0; - int len = strlen(str); - int sz = (len + 1) * 2; - char* buff = (char*) xcalloc(sz, 1); + int sz = target.size(); + QByteArray buff(sz, 0); while (! gbfeof(fin)) { char c = gbfgetc(fin); - memmove(buff, buff + 1, sz - 1); - buff[sz - 1] = c; + buff = buff.right(sz-1); + buff.append(c); if (c == 0) { eos++; if (eos >= 2) { /* two or more zero bytes => end of string */ - char* test = cet_str_uni_to_utf8((short*)buff, len); - if (test) { - res = (strcmp(str, test) == 0); - xfree(test); - if (res) { - break; - } + // QByteArray::compare introduced in Qt 5.12, but we can use + // QByteArray::startsWith as buff.size() == target.size(). + if (buff.startsWith(target)) { + return 1; } } } else { eos = 0; } } - xfree(buff); - return res; + return 0; } static void @@ -153,7 +149,7 @@ destinator_read_poi() garmin_fs_t* gmsd; if (count == 0) { - str = read_wcstr(0); + str = read_wcstr(); if ((str != DST_DYN_POI)) { fatal(MYNAME "_poi: Invalid record header!\n"); } @@ -165,12 +161,12 @@ destinator_read_poi() Waypoint* wpt = new Waypoint; - wpt->shortname = read_wcstr(0); - wpt->notes = read_wcstr(0); /* comment */ + wpt->shortname = read_wcstr(); + wpt->notes = read_wcstr(); /* comment */ - QString hnum = read_wcstr(0); /* house number */ + QString hnum = read_wcstr(); /* house number */ - str = read_wcstr(0); /* street */ + str = read_wcstr(); /* street */ if (str.isEmpty()) { str = hnum; hnum = QString(); @@ -184,19 +180,19 @@ destinator_read_poi() garmin_fs_t::set_addr(gmsd, str); } - if (!(str = read_wcstr(0)).isEmpty()) { /* city */ + if (!(str = read_wcstr()).isEmpty()) { /* city */ gmsd = gmsd_init(wpt); garmin_fs_t::set_city(gmsd, str); } - (void) read_wcstr(1); /* unknown */ + (void) read_wcstr(); /* unknown */ - if (!(str = read_wcstr(0)).isEmpty()) { /* postcode */ + if (!(str = read_wcstr()).isEmpty()) { /* postcode */ gmsd = gmsd_init(wpt); garmin_fs_t::set_postal_code(gmsd, str); } - (void) read_wcstr(1); /* unknown */ + (void) read_wcstr(); /* unknown */ (void) gbfgetdbl(fin); @@ -225,7 +221,7 @@ destinator_read_rte() while (!(gbfeof(fin))) { if (count == 0) { - QString str = read_wcstr(0); + QString str = read_wcstr(); if ((str != DST_ITINERARY)) { fatal(MYNAME "_itn: Invalid record header!\n"); } @@ -237,8 +233,8 @@ destinator_read_rte() Waypoint* wpt = new Waypoint; - wpt->shortname = read_wcstr(0); - wpt->notes = read_wcstr(0); + wpt->shortname = read_wcstr(); + wpt->notes = read_wcstr(); (void) gbfgetint32(fin); (void) gbfgetdbl(fin); @@ -469,12 +465,14 @@ static void destinator_rd_init(const QString& fname) { fin = gbfopen_le(fname, "rb", MYNAME); + utf16le_codec = QTextCodec::codecForName("UTF-16LE"); } static void destinator_rd_deinit() { gbfclose(fin); + utf16le_codec = nullptr; } static void @@ -502,12 +500,14 @@ static void destinator_wr_init(const QString& fname) { fout = gbfopen_le(fname, "wb", MYNAME); + utf16le_codec = QTextCodec::codecForName("UTF-16LE"); } static void destinator_wr_deinit() { gbfclose(fout); + utf16le_codec = nullptr; } static void diff --git a/igo8.cc b/igo8.cc index c89c3bea8..0a922327c 100644 --- a/igo8.cc +++ b/igo8.cc @@ -61,10 +61,21 @@ */ +#include +#include // for SEEK_SET +#include +#include // for atoi +#include // for memset + +#include // for QChar +#include // for QString +#include // for QVector +#include // for ushort + #include "defs.h" -#include "cet.h" -#include "cet_util.h" -#include +#include "gbfile.h" // for gbfwrite, gbfclose, gbfseek, gbfgetint32, gbfread, gbfile, gbfopen_le +#include "src/core/datetime.h" // for DateTime + #define FLOAT_TO_INT(x) ((int)((x) + ((x)<0?-0.5:0.5))) #define IGO8_HEADER_SIZE (sizeof(igo8_id_block) + 256) @@ -236,64 +247,33 @@ static void write_igo8_track_point(const Waypoint* wpt) point_count++; } -// Write src unicode str to the dst cstring using unicode characters -// All lengths are in bytes -static unsigned int print_unicode(char* dst, const unsigned int dst_max_length, short* src, unsigned int src_len) +// Write src unicode str to the dst cstring using unicode characters. +// dst_max_length is in bytes. +// I have no idea if iGo8 even supports real unicode 2, but is does look like +// it as every ascii character is a short with the ascii character as the +// least significant 7 bits. +static unsigned int print_unicode(char* dst, int dst_max_length, const QString& src) { - // Check to see what length we were passed, if the length doesn't include the null - // then we make it include the null - if (src[(src_len/2) - 1] != 0) { - // If the last character isn't null check the next one - if (src[(src_len/2)] != 0) { - // If the next character also inst' null, make it null - src[(src_len/2)] = 0; - } else { - // The next character is null, adjust the total length of the str to account for this - src_len += 2; - } - } - - // Make sure we fit in our dst size - if (src_len > dst_max_length) { - src_len = dst_max_length; - src[(src_len/2) - 1] = 0; // Make sure we keep that terminating null around + int max_qchars = dst_max_length / 2; + if (max_qchars < 1) { + // We must have room for the terminator. + fatal(MYNAME ": igo8 header overflow.\n"); + }; + // Write as many characters from the source as possible + // while leaving space for a terminator. + int n_src_qchars = std::min(max_qchars - 1, src.size()); + for (int i = 0; i < n_src_qchars; ++i) { + le_write16(dst, src.at(i).unicode()); + dst += 2; } + le_write16(dst, 0); // NULL (U+0000) terminator - // Copy the str - memcpy(dst, src, src_len); - - return src_len; -} - -// This is a sort of hacked together ascii-> unicode 2 converter. I have no idea -// if iGo8 even supports real unicode 2, but is does look like it as every ascii -// character is a short with the ascii character as the least significant 7 bits -// -// Please replace this with a much more filled out and correct version if you see -// fit. - -/* 2008/06/24, O.K.: Use CET library for ascii-> unicode 2 converter */ -// 2008/07/25, Dustin: Slight fix to make sure that we always null terminate the -// string, validate that the use of the CET library provides -// conforming output, remove my old junk converter code. - -static unsigned int ascii_to_unicode_2(char* dst, const unsigned int dst_max_length, const char* src) -{ - int len; - - short* unicode = cet_str_any_to_uni(src, &cet_cs_vec_ansi_x3_4_1968, &len); - - len *= 2; /* real size in bytes */ - len = print_unicode(dst, dst_max_length, unicode, len); - - xfree(unicode); - - return len; + return (n_src_qchars + 1) * 2; } static void write_header() { - char header[IGO8_HEADER_SIZE] = {'\0'}; + char header[IGO8_HEADER_SIZE] = {}; igo8_id_block tmp_id_block; p_igo8_id_block id_block = (p_igo8_id_block)header; uint32_t current_position = 0; @@ -334,13 +314,13 @@ static void write_header() if (igo8_option_title) { title = igo8_option_title; } - current_position += ascii_to_unicode_2((header+current_position), IGO8_HEADER_SIZE - current_position - 2, title); + current_position += print_unicode((header+current_position), IGO8_HEADER_SIZE - current_position - 2, title); // Set the description of the track if (igo8_option_description) { description = igo8_option_description; } - current_position += ascii_to_unicode_2((header+current_position), IGO8_HEADER_SIZE - current_position, description); + current_position += print_unicode((header+current_position), IGO8_HEADER_SIZE - current_position, description); gbfwrite(&header, IGO8_HEADER_SIZE, 1, igo8_file_out); } diff --git a/lowranceusr.cc b/lowranceusr.cc index 87eb8a593..6c656854d 100644 --- a/lowranceusr.cc +++ b/lowranceusr.cc @@ -95,6 +95,7 @@ #include // for QDate #include // for QDateTime #include // for QLatin1String +#include // for QScopedPointer #include // for QString, operator+, operator==, operator!= #include // for QTextCodec #include // for QTextEncoder @@ -558,9 +559,8 @@ lowranceusr4_writestr(const QString& buf, gbfile* file, int bytes_per_char) if (bytes_per_char == 1) { qba = buf.toUtf8(); } else { - QTextEncoder* encoder = utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader); + QScopedPointer encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader)); qba = encoder->fromUnicode(buf); - delete encoder; } int len = qba.size(); gbfputint32(len, file_out); diff --git a/mmo.cc b/mmo.cc index 649724312..cce4cfdbc 100644 --- a/mmo.cc +++ b/mmo.cc @@ -20,6 +20,7 @@ */ +#include // for assert #include // for isspace #include // for errno #include // for SEEK_CUR, fprintf, size_t, stdout @@ -34,12 +35,15 @@ #include // for QDateTime #include // for QHash, QHash<>::const_iterator #include // for QLatin1String +#include // for QScopedPointer #include // for QString, operator== +#include // for QTextCodec, QTextCodec::IgnoreHeader +#include // for QTextEncoder +#include // for QVector #include // for CaseInsensitive #include // for qAsConst, QAddConst<>::Type, foreach, Q_UNUSED #include "defs.h" -#include "cet.h" // for cet_ucs4_to_utf8, cet_utf8_to_ucs4 #include "gbfile.h" // for gbfputc, gbfgetuint16, gbfgetc, gbfgetdbl, gbfgetuint32, gbfputflt, gbfputuint32, gbfgetint16, gbfputdbl, gbfputuint16, gbfclose, gbfread, gbfseek, gbfputint16, gbfwrite, gbfcopyfrom, gbfeof, gbfgetflt, gbfgetint32, gbfile, gbfopen, gbfrewind, gbsize_t #include "session.h" // for curr_session, session_t #include "src/core/datetime.h" // for DateTime @@ -69,7 +73,7 @@ QVector mmo_args = { struct mmo_data_t { int objid; /* internal object id */ - char* name; + const char* name; const char* category; /* currently not handled */ gpsdata_type type; /* type of "data" */ time_t ctime; @@ -84,6 +88,8 @@ struct mmo_data_t { }; static gbfile* fin, *fout; +static QTextCodec* utf16le_codec{nullptr}; +static QTextCodec* legacy_codec{nullptr}; static int mmo_version; static int mmo_obj_ct; static int mmo_object_id; @@ -166,10 +172,10 @@ dbgprintf(const char* sobj, const char* fmt, ...) # define DBG(args) do {} while (0) ; #endif -static char* +static QString mmo_readstr() { - char* res; + QString res; signed int len = (unsigned)gbfgetc(fin); if (len == 0xFF) { @@ -181,18 +187,8 @@ mmo_readstr() // length is number of "characters" not number of bytes len = (unsigned)gbfgetc(fin); if (len > 0) { - unsigned int resbytes=0; - res = (char*) xmalloc(len*2 + 1); // bigger to allow for utf-8 expansion - for (signed int ii = 0; iitoUnicode(bytesin); return res; } // length zero is handled below: returns an empty string @@ -202,16 +198,9 @@ mmo_readstr() // positive values of len are for strings longer than 254, handled below: } // length zero returns an empty string - res = (char*) xmalloc(len + 1); - res[len] = '\0'; if (len) { - gbfread(res, len, 1, fin); - if (static_cast(len) != strlen(res)) { - // strlen requires a size_t, but Microsoft's stupid compiler doesn't - // do C99 %zd. Thanx, Microsoft. - fprintf(stdout, "got len %d but str is '%s' (strlen %d)\n", len, res, (int) strlen(res)); - fatal(MYNAME ": Error in file structure!\n"); - } + QByteArray bytesin = gbfreadbuf(len, fin); + res = legacy_codec->toUnicode(bytesin); } return res; @@ -419,10 +408,9 @@ mmo_read_CObjIcons(mmo_data_t* data) while ((icon_id = gbfgetuint32(fin))) { (void) gbfgetuint32(fin); (void) gbfgetuint32(fin); - char* name = mmo_readstr(); - DBG((sobj, "bitmap(0x%08X) = \"%s\"\n", icon_id, name)); - mmo_register_icon(icon_id, name); - xfree(name); + QString name = mmo_readstr(); + DBG((sobj, "bitmap(0x%08X) = \"%s\"\n", icon_id, qPrintable(name))); + mmo_register_icon(icon_id, CSTR(name)); // The next four bytes hold the length of the image, // read them and then skip the image data. gbfseek(fin, gbfgetuint32(fin), SEEK_CUR); @@ -446,7 +434,7 @@ mmo_read_CObjWaypoint(mmo_data_t* data) data->name, data->visible ? "yes" : "NO", data->objid)); data->data = wpt = new Waypoint; - wpt->shortname = QString::fromLatin1(data->name); + wpt->shortname = data->name; time_t time = data->mtime; if (! time) { @@ -486,34 +474,32 @@ mmo_read_CObjWaypoint(mmo_data_t* data) } - char* str = mmo_readstr(); /* descr + url */ - if (strncmp(str, "_FILE_ ", 7) == 0) { - char* cx = lrtrim(str + 7); - char* cend = strchr(cx, '\n'); - if (cend == nullptr) { - cend = cx + strlen(cx); - } + QString str = mmo_readstr(); /* descr + url */ + if (str.startsWith("_FILE_ ")) { + str.remove(0,7); + str = str.trimmed(); + int index = str.indexOf('\n'); - { - QString url = QString::fromUtf8(cx, cend-cx).trimmed(); - if (!url.isEmpty()) { - wpt->AddUrlLink(url); - } + QString url = str.mid(0, index).trimmed(); + if (!url.isEmpty()) { + wpt->AddUrlLink(url); } - if (*cend++) { - wpt->notes = QString::fromLatin1(cend); + if (index > 0) { + str.remove(0,index + 1); + if (!str.isEmpty()) { + wpt->notes = str; + } } if (wpt->HasUrlLink()) { DBG((sobj, "url = \"%s\"\n", wpt->url)); } - } else if (*str) { - wpt->notes = QString::fromLatin1(str); + } else if (!str.isEmpty()) { + wpt->notes = str; } - xfree(str); if (!wpt->notes.isEmpty()) { - DBG((sobj, "notes = \"%s\"\n", wpt->notes)); + DBG((sobj, "notes = \"%s\"\n", qPrintable(wpt->notes))); } mmo_fillbuf(buf, 12, 1); @@ -537,12 +523,10 @@ mmo_read_CObjWaypoint(mmo_data_t* data) } str = mmo_readstr(); /* name on gps ??? option ??? */ - if (*str) { + if (!str.isEmpty()) { wpt->description = wpt->shortname; wpt->shortname = str; - DBG((sobj, "name on gps = %s\n", str)); - } else { - xfree(str); + DBG((sobj, "name on gps = %s\n", qPrintable(str))); } int ux = gbfgetuint32(fin); @@ -720,9 +704,8 @@ mmo_read_CObjTrack(mmo_data_t* data) if (mmo_version >= 0x16) { // XXX ARB was u8 = gbfgetc(fin); but actually a string - char* text = mmo_readstr(); - DBG((sobj, "text = \"%s\"\n", text)); - xfree(text); + QString text = mmo_readstr(); + DBG((sobj, "text = \"%s\"\n", qPrintable(text))); uint16_t u16 = gbfgetuint16(fin); DBG((sobj, "unknown value = 0x%04X (since 0x16)\n", u16)); u16 = gbfgetuint16(fin); @@ -756,15 +739,13 @@ mmo_read_CObjText(mmo_data_t*) (void) lat; (void) lon; - char* text = mmo_readstr(); - DBG((sobj, "text = \"%s\"\n", text)); - xfree(text); + QString text = mmo_readstr(); + DBG((sobj, "text = \"%s\"\n", qPrintable(text))); mmo_fillbuf(buf, 28, 1); - char* font = mmo_readstr(); - DBG((sobj, "font = \"%s\"\n", font)); - xfree(font); + QString font = mmo_readstr(); + DBG((sobj, "font = \"%s\"\n", qPrintable(font))); mmo_fillbuf(buf, 25, 1); } @@ -794,15 +775,13 @@ mmo_read_CObjCurrentPosition(mmo_data_t*) } if (mmo_version >= 0x14) { - char* name = mmo_readstr(); - DBG((sobj, "name = \"%s\"\n", name)); - xfree(name); + QString name = mmo_readstr(); + DBG((sobj, "name = \"%s\"\n", qPrintable(name))); // XXX ARB was just: mmo_fillbuf(buf, 13, 1); // but actually it's string/long/string/long/long (void) gbfgetuint32(fin); name = mmo_readstr(); - DBG((sobj, "name = \"%s\"\n", name)); - xfree(name); + DBG((sobj, "name = \"%s\"\n", qPrintable(name))); (void) gbfgetuint32(fin); (void) gbfgetuint32(fin); } @@ -859,7 +838,7 @@ mmo_read_object() if (objid & 0x8000) { data = mmo_register_object(mmo_object_id++, nullptr, (gpsdata_type)0); - data->name = mmo_readstr(); + data->name = xstrdup(mmo_readstr()); if (objid != cat_object_id) { data->ctime = gbfgetuint32(fin); @@ -971,6 +950,9 @@ mmo_rd_init(const QString& fname) { fin = gbfopen_le(fname, "rb", MYNAME); + utf16le_codec = QTextCodec::codecForName("UTF-16LE"); + legacy_codec = QTextCodec::codecForName("Windows-1252"); + ico_object_id = pos_object_id = txt_object_id = cat_object_id = 0; wpt_object_id = rte_object_id = trk_object_id = 0; @@ -996,6 +978,9 @@ mmo_rd_deinit() } objects.clear(); + legacy_codec = nullptr; + utf16le_codec = nullptr; + gbfclose(fin); } @@ -1056,22 +1041,37 @@ mmo_register_category_names(const QString& name) static void -mmo_writestr(const char* str) +mmo_writestr(const QString& str) { - int ii, topbitset = 0; - int len = strlen(str); + + bool topbitset = false; // see if there's any utf-8 multi-byte chars - for (ii = 0; ii < len; ii++) { - if (str[ii] & 0x80) { - topbitset = 1; + QByteArray utf8 = str.toUtf8(); + int len = utf8.size(); + for (unsigned char byte : utf8) { + if (byte & 0x80) { + topbitset = true; break; } } // Old version can't handle utf-16 // XXX ARB check which version number can, just guessed at 0x12 if (mmo_version < 0x12) { - topbitset = 0; + topbitset = false; + } + + QByteArray outbytes; + if (topbitset) { + // Use an encoder to avoid generating a BOM. + QScopedPointer encoder(utf16le_codec->makeEncoder(QTextCodec::IgnoreHeader)); + outbytes = encoder->fromUnicode(str); + assert(outbytes.size() % 2 == 0); + len = outbytes.size() / 2; + len = len & 0xff; + } else { + outbytes = legacy_codec->fromUnicode(str); + len = outbytes.size(); } // XXX ARB need to convert UTF-8 into UTF-16 @@ -1089,30 +1089,13 @@ mmo_writestr(const char* str) } if (len) { if (topbitset) { - int utf16val; - int utf16len; - for (ii=0; iishortname ? wpt->shortname : "Mark")); int objid = mmo_write_obj_head("CObjWaypoint", - wpt->shortname.isEmpty() ? "Mark" : CSTRc(wpt->shortname), time, obj_type_wpt); + wpt->shortname.isEmpty() ? "Mark" : CSTR(wpt->shortname), time, obj_type_wpt); mmo_data_t* data = mmo_register_object(objid, wpt, wptdata); data->refct = 1; mmo_write_category("CCategory", (mmo_datatype == rtedata) ? "Waypoints" : "Marks"); @@ -1305,7 +1288,7 @@ mmo_write_rte_head_cb(const route_head* rte) time = gpsbabel_time; } int objid = mmo_write_obj_head("CObjRoute", - rte->rte_name.isEmpty() ? "Route" : CSTRc(rte->rte_name), time, obj_type_rte); + rte->rte_name.isEmpty() ? "Route" : CSTR(rte->rte_name), time, obj_type_rte); mmo_register_object(objid, rte, rtedata); mmo_write_category("CCategory", "Route"); gbfputc(0, fout); /* unknown */ @@ -1350,7 +1333,7 @@ mmo_write_trk_head_cb(const route_head* trk) return; } int objid = mmo_write_obj_head("CObjTrack", - trk->rte_name.isEmpty() ? "Track" : CSTRc(trk->rte_name), gpsbabel_time, obj_type_trk); + trk->rte_name.isEmpty() ? "Track" : CSTR(trk->rte_name), gpsbabel_time, obj_type_trk); mmo_write_category("CCategory", "Track"); gbfputuint16(trk->rte_waypt_ct, fout); @@ -1398,6 +1381,9 @@ mmo_wr_init(const QString& fname) { fout = gbfopen_le(fname, "wb", MYNAME); + utf16le_codec = QTextCodec::codecForName("UTF-16LE"); + legacy_codec = QTextCodec::codecForName("Windows-1252"); + mmo_object_id = 0x8000; mmo_obj_ct = 1; /* ObjIcons always present */ mmo_version = 0x12; /* by default we write as version 0x12 */ @@ -1427,6 +1413,9 @@ mmo_wr_deinit() } objects.clear(); + legacy_codec = nullptr; + utf16le_codec = nullptr; + gbfclose(fout); } diff --git a/testo b/testo index 66d133af4..561cdc89d 100755 --- a/testo +++ b/testo @@ -75,9 +75,9 @@ sort_and_compare() gpsbabel() { - ${PNAME} $* || { + ${PNAME} "$@" || { echo "$PNAME returned error $?" - echo "($PNAME $*)" + echo "($PNAME $@)" errorcount=`expr $errorcount + 1` #exit 1 } diff --git a/testo.d/igo8.test b/testo.d/igo8.test index 7e494eb15..b9ccf1076 100644 --- a/testo.d/igo8.test +++ b/testo.d/igo8.test @@ -13,3 +13,7 @@ compare ${TMPDIR}/igo.gpx ${TMPDIR}/new-igo2.gpx gpsbabel -i igo8 -f ${REFERENCE}/track/igo8_padded.trk -o gpx -F ${TMPDIR}/igo8_padded.gpx compare ${REFERENCE}/track/igo8_padded~gpx.gpx ${TMPDIR}/igo8_padded.gpx + +# test header. +gpsbabel -i igo8 -f ${REFERENCE}/track/igo8.trk -o igo8,title="Track 001",description="recorded track log",tracknum=3 -F ${TMPDIR}/igo8.trk +bincompare ${REFERENCE}/track/igo8.trk ${TMPDIR}/igo8.trk diff --git a/testo.d/mmo.test b/testo.d/mmo.test index 812f67903..73c12e969 100644 --- a/testo.d/mmo.test +++ b/testo.d/mmo.test @@ -7,7 +7,13 @@ compare ${REFERENCE}/memory-map~mmo.gpx ${TMPDIR}/memory-map~mmo.gpx # reading version 24 (0x18) gpsbabel -i mmo -f ${REFERENCE}/memory-map_v24.mmo -o gpx -F ${TMPDIR}/memory-map_v24~mmo.gpx compare ${REFERENCE}/memory-map_v24~mmo.gpx ${TMPDIR}/memory-map_v24~mmo.gpx -#writing (check only for memory leaks) +# writing (check only for memory leaks) gpsbabel -i gpx -f ${REFERENCE}/memory-map~mmo.gpx -o mmo -F ${TMPDIR}/memory-map~mmo.mmo gpsbabel -i mmo -f ${TMPDIR}/memory-map~mmo.mmo -o gpx -F ${TMPDIR}/memory-map~mmo~gpx.mmo +# we can only write version 17 (0x11) or 18 (0x12). +# we don't have a reference file from these versions, so we will create one and +# see if we can write it out without change. +gpsbabel -i mmo -f ${REFERENCE}/memory-map.mmo -o mmo -F ${TMPDIR}/memory-map.mmo +gpsbabel -i mmo -f ${TMPDIR}/memory-map.mmo -o mmo -F ${TMPDIR}/memory-map~mmo.mmo +bincompare ${TMPDIR}/memory-map.mmo ${TMPDIR}/memory-map~mmo.mmo -- 2.30.2